说明
虽然我将这篇文章归到 HTML5 API 这一分类中,但确切的说 PWA 并不是单一API,而是对多种API的综合应用。特别的,一个完整的 PWA 需要两个特别的文件——服务线程(一般为 service-worker.js )和Web应用清单(一般为 manifest.json或 manifest.webmanifest ),本文将主要对这两个文件展开介绍。
服务线程(service worker)
服务线程(service worker),是独立与页面的 WebWorker,其在启动后,会一直在后台运营(直到浏览器被关闭,或在一段时间内,没有该应用打开的的页面),并且可以拦截 Web 请求,将本地缓存甚至是即时生成的数据作为相应返回给原页面。
服务线程上下文环境
因为所有页面的请求可能都要经过服务线程,如果服务线程一但被堵塞,将会导致所有页面的请求都被堵塞,所以服务线程中的所有API均为异步API。
服务线程上下文环境是ServiceWorkerGlobalScope的实例,除了ECMAScript定义的语法级对象外,主要还有以下几个属性:
self: 指上下文环境本身,类似普通浏览器环境下的window对象,继承自WorkerGlobalScopecaches:Cache API,同普通浏览器环境下的caches对象,继承自WorkerGlobalScopefetch: 用于网络请求的函数,在服务线程中不支持XMLHttpRequest,请用fetch方法函数,继承自WorkerGlobalScope,但进行了重载clients: 其有打开窗口及获取已有窗口等的异步方法registration: 当前服务线程的状态等skipWaiting: 在服务线程脚本更新后,用于强制重起服务线程
另外有与windows中同名同用法(或功能相似)的对象,均继承自WorkerGlobalScope,具体见下表:
| 对象 | - | - | - |
|---|---|---|---|
btoa |
location |
indexedDB |
addEventListener |
atob |
setTimeout |
navigator |
removeEventListener |
crypto |
setInterval |
performance |
dispatchEvent |
fonts |
clearTimeout |
importScripts |
createImageBitmap |
origin |
clearInterval |
queueMicrotask |
但因为上下文环境的不通,部分API不支持或缺少部分属性成员,如navigator没有keyboard、clipboard、cookieEnabled等成员。
事件
ServiceWorkerGlobalScope 主要有以下几个事件:
install: 服务线程安装事件activate: 服务线程激活事件fetch: 页面或其他线程发起网络请求时的事件pushsubscriptionchange: 推送订阅改变事件,属Push APIpush: 推送事件,属Push APImessage: 消息事件,postMessage触发的事件notificationclick: 通知点击事件,属Notification APInotificationclose: 通知关闭事件,属Notification API
caches
ServiceWorkerGlobalScope.caches 是 CacheStorage 的实例,主要有以下几个方法:
match(request, options): 返回所有缓存库中第一个与请求匹配的Response,返回值为Promise<Response>open(name): 打开一个缓存库,返回值为Promise<Cache>delete(name): 删除一个缓存库,返回值为Promise<boolean>has(name): 判断是否存在指定的缓存库,返回值为Promise<boolean>keys(): 获取所有的缓存库名称,返回值为Promise<string[]>
Cache 实例
通过 ServiceWorkerGlobalScope.caches.open 得到一个 Cache实例,所有的缓存操作均在此对象上实现。其实例主要有以下几个方法:
match(request, options): 返回缓存库中第一个与请求匹配的Response,返回值为Promise<Response>matchAll(request, options): 返回缓存库中所有与请求匹配的Response,返回值为Promise<Response[]>add(request): 将请求及对应的Response加入到缓存库中,Response将通过自动fetch获得addAll(requests): 将多个请求及对应的Response加入到缓存库中,Response将通过自动fetch获得put(request, response): 将指定的请求及对应相应关联后加入到缓存库中delete(request, options): 删除与请求匹配的缓存
install 事件与 activate 事件
由于事件是通过继承EventTarget类实现的,所以,使用传统的方式,均会忽略事件回调函数返回的Promise对象,但在服务线程的安装、激活过程中,可能需要一些异步的操作,例如提前缓存部分页面。为确保触发install、activate 事件后,能够保证相关操作完成后再结束这两种状态,install 事件与 activate 事件提供了 waitUntil 方法。
waitUntil 方法接收一个 Promise 对象作为参数,当该 Promise 对象状态转为 fulfilled,rejected后,install、activate状态才会结束,单如果不调用waitUntil 方法则直接结束。
fetch 事件
fetch 事件中可以劫持其他页面祸线程发起的网络请求,返回一个自定义的相应。
如果需要返回自定义请求,需要劫持原有请求,需要调用fetch 事件的 respondWith 方法。
respondWith 方法接收一个 Promise<Response> 对象作为参数,该Response对象将作为原请求的Response。
此Response对象,可以是在fetch 事件中调用fetch方法得到的Response,也可以是caches中缓存的Response,甚至是可以通过调用Response构造方法创建的Response对象。
其他说明
目前,服务线程只能采用caches和indexedDB两种存储方式。
Web应用清单 (Web App Manifest)
Web应用清单主要提供有关应用程序的信息(例如名称、图标和描述)。本质为JSON文件,其MIME建议为application/manifest+json
其主要有以下几个属性:
name: 必须,应用的名称short_name: 应用的简称,一般用于主屏幕名称start_url: 必须,应用的启动地址display: 应用的像是方式, 目前支持:browser: 按照常规网页打开,默认值minimal-ui: 只保留控制导航的UI元素standalone: 按照普通应用的形式显示fullscreen: 全屏显示
background_color: 浏览器自动生产的启动页的背景色,为 CSS颜色值description: 应用的描述icons: 必须,应用的图标,用于桌面图标及启动页面,是一个数组,每个成员有以下几个属性:src: 必须,图标的地址sizes: 必须,包含空格分隔图像尺寸的字符串type: 图标的mine类型
orientation: 显示方向,可以是以下值之一:anynaturallandscapelandscape-primarylandscape-secondaryportraitportrait-primaryportrait-secondary
scope: 定义此网站上下文的导航范围,如果超出这个范围将按照普通页面展示theme_color: 网站的默认主题色
启动服务线程并加入清单文件
加入清单文件,只需要一个link标签即可,如:
<link rel="manifest" href="/manifest.json">
启动服务线程需要通过JavaScript实现:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js', {scope: '/'}).then(function (registration) {
// 注册成功
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function (err) {
// 注册失败 :(
console.log('ServiceWorker registration failed: ', err);
});
}